package cn.milolab.djback.service;

import cn.milolab.djback.bean.entity.Employee;
import cn.milolab.djback.bean.entity.Job;
import cn.milolab.djback.bean.entity.JobRecord;
import cn.milolab.djback.bean.request.*;
import cn.milolab.djback.conf.DjException;
import cn.milolab.djback.dao.EmployeeDAO;
import cn.milolab.djback.dao.JobDAO;
import cn.milolab.djback.dao.JobRecordDAO;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * @author 叶欣委
 */
@Service
public class JobService {
    @Autowired
    JobDAO jobDAO;
    @Autowired
    JobRecordDAO jobRecordDAO;
    @Autowired
    EmployeeDAO employeeDAO;

    /**
     * 添加活动
     * @param addJobRequest
     */
    @Transactional(rollbackFor = Exception.class)
    public void addJob(AddJobRequest addJobRequest){
        checkNotNull(addJobRequest, "请求参数为空");
        checkNotNull(addJobRequest.getStartTime(), "活动开始时间不能为空");
        checkNotNull(addJobRequest.getEndTime(), "活动结束时间不能为空");
        checkNotNull(addJobRequest.getName(), "活动名称不能为空");
        checkNotNull(addJobRequest.getMaxPeopleCount(), "活动人数限制不能为空");
        //判断输入的时间是否合法
        if (addJobRequest.getStartTime().compareTo(addJobRequest.getEndTime()) > 0) {
            throw DjException.requestLogicError("活动时间输入有误");
        }
        //添加活动到数据库中
        Integer res = jobDAO.insertJob(addJobRequest);
        //判断是否添加成功
        if (res != 1) {
            throw DjException.internalServerError("无法添加活动");
        }
    }

    /**
     * 修改活动
     * @param updateJobRequest
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateJob(UpdateJobRequest updateJobRequest){
        checkNotNull(updateJobRequest, "请求参数为空");
        checkNotNull(updateJobRequest.getStartTime(), "活动开始时间不能为空");
        checkNotNull(updateJobRequest.getEndTime(), "活动结束时间不能为空");
        //判断输入的时间是否合法
        if (updateJobRequest.getStartTime().compareTo(updateJobRequest.getEndTime()) > 0) {
            throw DjException.requestLogicError("活动时间输入有误");
        }
        //在数据库中修改活动
        Integer res = jobDAO.updateJob(updateJobRequest);
        //判断是否修改成功
        if ( res!=1 ) {
            throw DjException.internalServerError("无法修改活动");
        }
    }

    /**
     * 删除活动
     * @param id
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteJob( Integer id){
        Integer res1 = jobDAO.deleteJob(id);
        Integer res2 = jobRecordDAO.deleteJobRecordByJobId(id);
        if (res1 != 1) {
            throw DjException.internalServerError("无法删除活动");
        }
    }

    /**
     * 查看活动
     * @param id
     * @param sort
     * @param page
     * @param limit
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String,Object> getJob(Integer id ,  String sort ,  Integer page,  Integer limit){
        checkNotNull(sort,"排序类型不能为空");
        checkNotNull(page,"页数不能为空");
        checkNotNull(limit,"页大小不能为空");
        //注意：此处开启分页一定要在数据库查询之前调用，否则会不起作用
        PageHelper.startPage(page,limit);
        //查找活动
        List<Job> jobList = jobDAO.getJob(sort);
        //如果传过来id的话直接进行过滤
        if (!StringUtils.isEmpty(id)) {
            jobList.removeIf(job -> !job.getId().equals(id));
        }
        //每个活动的人员信息
        List<List<Employee>>  info = new ArrayList<>();
        //通过每个活动的id来查找值班的人
        jobList.forEach(job -> {
            List<Employee> employeeList = new ArrayList<>(10);
            //得到每一个活动值班的人的employeeId
            List<Integer> employeeId = jobRecordDAO.getEmployeeIdByJobId(job.getId());
            for (Integer ids : employeeId) {
                //通过employeeId找到值班人员的完整信息并放入employeeList中
                Employee employeeById = employeeDAO.getEmployeeById(ids);
                employeeList.add(employeeById);
            }
            //将每一场活动的人员信息分别放入list中
            info.add(employeeList);
        });
        //将查到的信息进行分页
        PageInfo<Job> jobPageInfo = new PageInfo<>(jobList);
        PageInfo pageInfo = new PageInfo(info);
        //将分页后的结果放入map中返回
        HashMap<String,Object> result = new HashMap<>(2);
        result.put("job",jobPageInfo.getList());
        result.put("info",pageInfo.getList());
        return result;
    }

    /**
     * 获取我的班次
     * @param userId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public List<Job> getMyJob(Integer userId) {
        checkNotNull(userId,"参数不能为空");
        //通过userid获得employee对象
        Employee employee = employeeDAO.getEmployeeByUserId(userId);
        //通过employeeId得到jobId
        List<Integer> jobId = jobRecordDAO.getJobIdByEmployeeId(employee.getId());
        //通过jobId得到Job
        List<Job> result = new ArrayList<Job>();
        jobId.forEach(id -> {
            //通过jobId得到job的具体信息
            Job job = jobDAO.getJobById(id);
            //将job放入list中
            result.add(job);
        });
        return result;
    }

    /**
     * 申请抢班
     * @param applyJobRequest
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    public void applyJob(ApplyJobRequest applyJobRequest, Integer userId) {
        //判断输入的时间是否合法
        if (applyJobRequest.getStartTime().compareTo(applyJobRequest.getEndTime()) > 0) {
            throw DjException.requestLogicError("抢班时间非法输入");
        }
        //通过userId获得employee
        Employee employee = employeeDAO.getEmployeeByUserId(userId);
        checkNotNull(employee,"无法找到该社员");
        //通过jobId获得job
        Job job = jobDAO.getJobById(applyJobRequest.getJobId());
        checkNotNull(job,"无法找到该班次");
        //通过job获得班次的最大值班人数
        Integer maxPeopleCount = job.getMaxPeopleCount();
        //通过jobId获得所有抢班记录
        List<JobRecord> jobRecordList = jobRecordDAO.getJobRecordByJobId(job.getId());
        //判断是否抢过同一时间的班次
        jobRecordList.forEach(jobRecord -> {
            if (!(jobRecord.getStartTime().compareTo(applyJobRequest.getEndTime()) >= 0
                    || jobRecord.getEndTime().compareTo(applyJobRequest.getStartTime()) <= 0)) {
                if(jobRecord.getEmployeeId().equals(employee.getId())){
                    throw DjException.requestLogicError("同一个人无法抢同一时间的班次");
                }
            }
        });
        //所有值班人员的开始时间
        List<Date> startTime = jobRecordList.stream().map(JobRecord::getStartTime).collect(Collectors.toList());
        //将抢班的时间加入list中
        startTime.add(applyJobRequest.getStartTime());
        //所有值班人员的结束时间
        List<Date> endTime = jobRecordList.stream().map(JobRecord::getEndTime).collect(Collectors.toList());
        //将抢班的时间加入list中
        endTime.add(applyJobRequest.getEndTime());
        //进行排序
        List<Date> start = sort(startTime);
        List<Date> end = sort(endTime);

        Integer num = 0;
        Integer max = 0;
        //判断同一时间段最多的人数
        for (int i=0 ,j=0 ;i<start.size();) {
            if (start.get(i).compareTo(end.get(j)) < 0) {
                i++;
                num++;
                max = Math.max(max,num);
            } else {
                j++;
                num--;
            }
        }
        //声明是抢班
        Integer isArrange = 0;
        //判断此班次的人数是否超过了最大值班人数
        if (max>maxPeopleCount) {
            throw DjException.requestLogicError("此段时间人数已满");
        } else {
            Integer res = jobRecordDAO.insertJobRecord(applyJobRequest.getJobId(),applyJobRequest.getStartTime(),applyJobRequest.getEndTime(), employee.getId(),isArrange);
            if (res != 1) {
                throw DjException.internalServerError("抢班失败");
            }
        }
    }

    /**
     * 获取开放班次
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> getOpeningJob() {
        //获取当前时间
        Date date = new Date();
        System.out.println(date);
        //找出当前时间还未结束的活动
        List<Job> jobs = jobDAO.getOpeningJob(date);
        //每个活动的人员信息
        List<List<Employee>>  info = new ArrayList<List<Employee>>();
        //通过每个活动的id来查找值班的人
        for (Job job : jobs) {
            List<Employee> employees = new ArrayList<>();
            //得到每一个活动值班的人的employeeId
            List<Integer> employeeId = jobRecordDAO.getEmployeeIdByJobId(job.getId());
            //通过employeeId找到值班人员的完整信息并放入list中
            for (Integer ids : employeeId) {
                Employee employeeById = employeeDAO.getEmployeeById(ids);
                employees.add(employeeById);
            }
            //将每一场活动的人员信息分别放入list中
            info.add(employees);
        }
        //将信息放入hash中
        HashMap<String,Object> result = new HashMap<>(2);
        result.put("job",jobs);
        result.put("info",info);
        return result;
    }

    /**
     * 换班
     * @param exchangeJobRequest
     */
    @Transactional(rollbackFor = Exception.class)
    public void exchangeJob(ExchangeJobRequest exchangeJobRequest) {
        try {
            jobRecordDAO.updateEmployeeIdById(exchangeJobRequest);
        } catch (Exception e) {
            throw DjException.internalServerError("换班失败");
        }

    }

    /**
     * 排班
     * @param arrangeJobRequest
     */
    @Transactional(rollbackFor = Exception.class)
    public void arrangeJob(ArrangeJobRequest arrangeJobRequest) {
        //判断输入的时间是否合法
        if (arrangeJobRequest.getStartTime().compareTo(arrangeJobRequest.getEndTime()) > 0) {
            throw DjException.requestLogicError("抢班时间非法输入");
        }
        //通过jobId获得班次的最大值班人数得到job
        Job job = jobDAO.getJobById(arrangeJobRequest.getJobId());
        //通过job得到maxPeopleCount
        Integer maxPeopleCount = job.getMaxPeopleCount();
        //通过jobId获得所有抢班记录
        List<JobRecord> jobRecordList = jobRecordDAO.getJobRecordByJobId(job.getId());
        //判断是否抢过同一时间的班次
        jobRecordList.forEach(jobRecord -> {
            if (!(jobRecord.getStartTime().compareTo(arrangeJobRequest.getEndTime()) >= 0
                    || jobRecord.getEndTime().compareTo(arrangeJobRequest.getStartTime()) <= 0)) {
                if(jobRecord.getEmployeeId().equals(arrangeJobRequest.getEmployeeId())){
                    throw DjException.requestLogicError("同一个人无法抢同一时间的班次");
                }
            }
        });
        //所有值班人员的开始时间
        List<Date> startTime = jobRecordList.stream().map(JobRecord::getStartTime).collect(Collectors.toList());
        //将抢班的时间加入list中
        startTime.add(arrangeJobRequest.getStartTime());
        //所有值班人员的结束时间
        List<Date> endTime = jobRecordList.stream().map(JobRecord::getEndTime).collect(Collectors.toList());
        //将抢班的时间加入list中
        endTime.add(arrangeJobRequest.getEndTime());
        //进行排序
        List<Date> start = sort(startTime);
        List<Date> end = sort(endTime);

        Integer num = 0;
        Integer max = 0;
        //判断同一时间段最多的人数
        for(int i=0 ,j=0 ;i<start.size();){
            if(start.get(i).compareTo(end.get(j))<0){
                i++;
                num++;
                max = Math.max(max,num);
            }else{
                j++;
                num--;
            }
        }

        //声明是排班的
        Integer isArrange = 1;
        //判断此班次的人数是否超过了最大值班人数
        if(max>maxPeopleCount){
            throw DjException.requestLogicError("此段时间人数已满");
        }else{
            Integer res = jobRecordDAO.insertJobRecord(arrangeJobRequest.getJobId(), arrangeJobRequest.getStartTime(), arrangeJobRequest.getEndTime(), arrangeJobRequest.getEmployeeId(), isArrange);
            if (res != 1) {
                throw DjException.internalServerError("排班失败");
            }
        }

    }

    /**
     * 排序
     * @param list
     * @return
     */
    private List<Date> sort(List<Date> list){
        Integer n = list.size();
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                if(list.get(i).compareTo(list.get(j))>0){
                    Date t = list.get(i);
                    list.set(i,list.get(j));
                    list.set(j,t);
                }
            }
        }
        return list;
    }



}
